Open
Conversation
Ports nativeapptemplate/NativeAppTemplate-iOS#49 - ItemTag model: queueNumber → name; add description/position; remove scanState/customerReadAt/alreadyCompleted - ItemTagAdapter: parse name/description/position; drop legacy attrs - PermissionsRequest: maximum_queue_number_length meta now optional with default 256 (full removal deferred to 2A-3) - SessionController / App.swift null stub: maximumQueueNumberLength default 0 → 256 - ViewModels: queueNumber → name; validateQueueNumberLength → validateNameLength; hasInvalidDataQueueNumber → hasInvalidDataName - Drop 5 production call sites of the deleted fields (ShowTagInfoScanResultView, ScanViewModel, ShopDetailViewModel, ShopDetailCardView, MainViewModel) — each flagged // TODO: removed in Phase 2A-2 - Shop model: drop displayShopServerPath / displayShopServerUrl; drop "Open Server Number Tags Webpage" link from ShopDetailView; delete NumberTagsWebpageListView/ViewModel and the Shop Settings section that linked to it - Tests: update helpers and adapter fixtures for the new schema; delete ScanViewModelTest (queue/NFC-specific, goes away in 2A-2), completeTagWhenAlreadyCompleted test (asserted the removed branch), and NumberTagsWebpageListViewModelTest Main target and test target both compile. Some tests may fail at runtime (deferred to Phase 2A-4 per plan). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-ios-paid-itemtag-schema Phase 2A-1: Port ItemTag schema update from paid iOS
Ports nativeapptemplate/NativeAppTemplate-iOS#50 to the Free iOS app. Drops all NFC, QR-code, and Scan-tab functionality; ItemTagDetailView becomes a placeholder until Phase 2A-3 lands the full generic CRUD UI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reverts the upstream PR's removal of the complete/idle swipe actions on ShopDetailView so users can still mark items completed or send them back to idled without going through Settings. Renames the user-facing "reset" terminology to "idle" since the action just transitions a completed tag back to the idled state: - ItemTag UI/ViewModel/strings: resetTag → idleTag, isResetting → isIdling, itemTagReset[Error] → itemTagIdled[Error], button label "Reset" → "Idle". - ItemTag repository layer: ItemTagRepositoryProtocol.reset → idle, ItemTagRepository / DemoItemTagRepository / TestItemTagRepository updated. - ItemTag network layer: ResetItemTagRequest → IdleItemTagRequest, ItemTagsService.resetItemTag → idleItemTag, HTTP path /shopkeeper/item_tags/:id/reset → /shopkeeper/item_tags/:id/idle. Backend must expose PATCH /shopkeeper/item_tags/:id/idle. Shop repository / network layer left as `reset` (no UI surfaces it). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…an-removal Phase 2A-2: Remove NFC / QR / Scan
Ports nativeapptemplate/NativeAppTemplate-iOS#52. The swipe action already reflects the state change via ShopDetailCardView's re-render after reload(); the extra success toast was redundant noise. Errors still post to the message bus so users still see feedback when something actually fails. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ag-success-toasts Silence success toasts on item-tag complete/idle in ShopDetailView
Ports nativeapptemplate/NativeAppTemplate-iOS#53 to the Free iOS app. Replaces the Phase 2A-2 ItemTagDetailView placeholder with a full generic CRUD UI; relaxes Create/Edit validation; adds a description field; makes ItemTag.position non-optional. Schema: - ItemTag.position: Int? -> Int = 0; toJson() excludes position (server auto-assigns). - ItemTagAdapter strict-guards position; throws invalidOrMissingAttributes when missing. - Mock ItemTag(position: nil) -> position: 1 across tests. Rename: - maximumQueueNumberLength -> maximumNameLength across SessionController, protocol, App.swift NullSessionController, TestSessionController, PermissionsRequest. Default fallback 256 -> 100. - PermissionsRequest reads meta["maximum_name_length"]. Create + Edit: - Validation: name 1-100 chars (any string), description 0-1000 chars. Drops alphanumeric and count >= 2 checks; standard keyboard. - New description field with multi-line TextEditor. - Edit's hasInvalidData also requires name OR description changed. Detail view full rewrite: - State badge (IdlingTag / CompletedTag), description display, completedAt timestamp. - Toggle button (Mark as completed / Mark as idled) calling ItemTagRepository.complete(id:) / idle(id:). - New isToggling, completeItemTag(), idleItemTag(); failure posts error toast, success silently re-renders (matching PR #52). ItemTagListCardView: name + description preview (2 lines) + state badge + completedAt. Constants: removed tagNumber/tagNumberIsInvalid; added nameLabel, descriptionLabel, itemTagNamePlaceholder, itemTagNameIsInvalid, itemTagDescriptionIsInvalid, completedAtLabel, markAsCompleted, markAsIdled, itemTagNameHelp/itemTagDescriptionHelp helpers, and NativeAppTemplateConstants.maximumItemTagDescriptionLength = 1_000. Deviations from upstream PR #53: - Skipped permission gates (canUpdateShops/canDeleteShops/canCreateShops) - Free repo has no Permission system. Edit/delete/toggle/create are unconditionally available. Skipped the upstream permissionChecking parameterized tests for the same reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new hasInvalidDataChangeDetection test in ItemTagEditViewModelTest sets name = "Updated" (7 chars), which tripped the old default of 4 (carried over from the queue-number-length era). Match upstream Paid PR's default of 100. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-ios-paid-generic-crud-ui Phase 2A-3: Generic CRUD UI for ItemTag
Ports nativeapptemplate/NativeAppTemplate-iOS#54. ItemTagDetailView's completed-timestamp row was using Text(completedAt.formatted()), which is locale-dependent — same instant rendered as 4/26/2026, 10:30 AM (en_US), 26/04/2026, 10:30 (en_GB), or 2026/04/26 10:30 (ja_JP). Non-Gregorian calendars (Buddhist, Japanese imperial) could shift the year value entirely. Introduces Date.cardDateTimeString that stitches existing cardDateString + cardTimeString format constants, and pins Locale(identifier: "en_US_POSIX") on the shared DateFormatter.formatter(for:) factory so every fixed-format formatter is calendar/locale-stable. - DateFormatter+Extensions.swift: - cardDateString: "MMM dd yyyy" → "yyyy/MM/dd" (the MMM token was locale-aware; the constant had zero callers). - formatter(for:) factory: pins Locale(identifier: "en_US_POSIX"). - Date+Extensions.swift: new cardDateTimeString computed property. - ItemTagDetailView, ShopDetailCardView, ItemTagListCardView: switched to cardDateTimeString. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-time-string Display completedAt as yyyy/MM/dd HH:mm regardless of locale
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The associated-domains entitlement is no longer needed; the entitlements file is now empty and dropped. CODE_SIGN_ENTITLEMENTS unset for both Debug and Release configs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eous Miscellaneous cleanup
Ports nativeapptemplate/NativeAppTemplate-iOS#59. Two-step cleanup of maximumNameLength: 1. Stop reading maximum_name_length from /shopkeeper/permissions. The client already tolerated its absence via a "?? 100" fallback, so this is dead plumbing. Drops the field from PermissionsResponse, the meta read in PermissionsRequest.handle, and the assignment in SessionController.fetchPermissions. 2. Move maximumNameLength from SessionController to Constants. Now that it's a fixed value, it's just a constant — like maximumItemTagDescriptionLength. Adds NativeAppTemplateConstants.maximumItemTagNameLength = 100, drops maximumNameLength from SessionControllerProtocol / SessionController / NullSessionController / TestSessionController. ItemTagCreateViewModel and ItemTagEditViewModel now read the constant directly and no longer take sessionController. Updates the call sites in ItemTagListView / ItemTagDetailView. Tests rewritten to drop sessionController field and update truncation tests to use 100+ char strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mum-name-length-from-permissions Drop maximum_name_length from permissions; move to Constants
Ports nativeapptemplate/NativeAppTemplate-iOS#60. Mirror the ItemTag pattern from PR #49 for Shop. Server has no caps on Shop name/description; this is a client-only UX guard. - Constants: maximumShopNameLength = 100, maximumShopDescriptionLength = 1_000. New strings: shopNameIsInvalid, shopDescriptionIsInvalid, plus shopNameHelp(maximumLength:) / shopDescriptionHelp(maximumLength:) parametric helpers. - ShopCreateViewModel + ShopBasicSettingsViewModel: split hasInvalidData into hasInvalidDataName + hasInvalidDataDescription, expose maximumNameLength / maximumDescriptionLength, add validateNameLength() / validateDescriptionLength(). - ShopCreateView + ShopBasicSettingsView: wire .onChange truncation on Name and Description; switch to two-line footer (always-visible help + conditional red "is invalid" text), matching ItemTagCreateView. - Tests: added maximumNameLength, maximumDescriptionLength, parametric nameValidation / descriptionValidation, and the two truncation tests on both viewmodel test suites; replaced the old simple hasInvalidData(name:) parametric test in ShopCreateViewModelTest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-description-caps Add client-side length caps + truncation for Shop name/description
- Drop scannedItemTagsCount from Shop model, adapter, demo repo, and tests; remove the "tags scanned by customers" stat from ShopListCardView.
- Replace ShopDetailView "Learn More" link with a left-aligned shopDetailInstruction ("Swipe an item tag to change its status.").
- Swap the OnboardingView "How to use" link for the Support Website link; trim onboarding from 13 to 8 descriptions and drop unused String(localized:) wrap.
- Normalize ItemTag string keys: editTag/addTag/deleteTag/etc. -> editItemTag/addItemTag/deleteItemTag/etc.
- Drop unused constants: howToUseUrl, learnMore, createShops, createTags.
…-scanned-tighten-itemtag-labels Drop unused tags-scanned count and howToUse; tighten ItemTag labels
Convert the large `extension String` block in `Constants.swift` to a caseless `enum Strings` namespace, matching the existing `NativeAppTemplateConstants` idiom in the same file. Inline the two `cardDateString`/`cardTimeString` literals in `DateFormatter+Extensions.swift` and remove the small extension they lived in. Update call sites across the app and tests from `String.foo` to `Strings.foo`, including dot-shorthand uses for `message:`, `text:`, `buttonTitle:`, and `?.message ==` parameters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ng-constants-with-enum Wrap String constants in enum Strings namespace
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotes the `substrate-v2` working branch to `main`. After this merge, the Free iOS app is a generic parent-child CRUD shell (Shops + ItemTags) with no NFC / QR / scan dependency, ready to fork for arbitrary domains.
This rolls up six already-reviewed PRs that have already landed on `substrate-v2`:
High-level changes
Backend coordination
The Rails backend must be on a release that:
Test plan
🤖 Generated with Claude Code